package casbin_v2

import (
	"errors"
	"goskeleton/app/global/my_errors"
	"goskeleton/app/global/variable"
	"strings"
	"time"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// 创建 casbin Enforcer(执行器)
func InitCasbinEnforcer() (*casbin.SyncedEnforcer, error) {
	var tmpDbConn *gorm.DB
	var Enforcer *casbin.SyncedEnforcer
	switch strings.ToLower(variable.ConfigGormv2Yml.GetString("Gormv2.UseDbType")) {
	case "mysql":
		if variable.GormDbMysql == nil {
			return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
		}
		tmpDbConn = variable.GormDbMysql
	case "sqlserver", ",mssql":
		if variable.GormDbSqlserver == nil {
			return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
		}
		tmpDbConn = variable.GormDbSqlserver
	case "postgre", "postgresql", "postgres":
		if variable.GormDbPostgreSql == nil {
			return nil, errors.New(my_errors.ErrorCasbinCanNotUseDbPtr)
		}
		tmpDbConn = variable.GormDbPostgreSql
	default:
	}

	prefix := variable.ConfigYml.GetString("Casbin.TablePrefix")
	tbName := variable.ConfigYml.GetString("Casbin.TableName")
	fullTableName := prefix + "_" + tbName

	// 为兼容非标准表结构，使用明确的表名适配器
	variable.ZapLog.Info("Casbin适配器配置",
		zap.String("prefix", prefix),
		zap.String("table_name", tbName),
		zap.String("full_table_name", fullTableName))

	// 首先检查表是否存在以及表结构
	var tableExists bool
	checkErr := tmpDbConn.Raw("SELECT COUNT(*) > 0 FROM information_schema.tables WHERE table_name = ?", fullTableName).Scan(&tableExists).Error
	if checkErr != nil {
		variable.ZapLog.Warn("无法检查表存在性", zap.Error(checkErr))
	}

	variable.ZapLog.Info("表存在性检查",
		zap.String("table_name", fullTableName),
		zap.Bool("exists", tableExists))

	// 创建适配器的多种策略
	var a *gormadapter.Adapter
	var err error

	// 策略1: 强制使用自定义表名适配器（针对非标准表结构）
	// variable.ZapLog.Info("尝试策略1：自定义表名适配器")
	a, err = gormadapter.NewAdapterByDBUseTableName(tmpDbConn, prefix, tbName)
	if err != nil {
		variable.ZapLog.Error("策略1失败：自定义表名适配器创建失败", zap.Error(err))

		// 策略2: 使用带完整表名的自定义适配器
		// variable.ZapLog.Info("尝试策略2：完整表名适配器")
		a, err = gormadapter.NewAdapterByDBUseTableName(tmpDbConn, "", fullTableName)
		if err != nil {
			variable.ZapLog.Error("策略2失败：完整表名适配器创建失败", zap.Error(err))

			// 策略3: 使用标准适配器（最后的选择）
			// variable.ZapLog.Info("尝试策略3：标准适配器")
			a, err = gormadapter.NewAdapterByDB(tmpDbConn)
			if err != nil {
				variable.ZapLog.Error("所有策略都失败：无法创建Casbin适配器",
					zap.Error(err),
					zap.String("prefix", prefix),
					zap.String("table_name", tbName),
					zap.String("full_table_name", fullTableName),
					zap.String("db_type", variable.ConfigGormv2Yml.GetString("Gormv2.UseDbType")))
				return nil, errors.New(my_errors.ErrorCasbinCreateAdaptFail + ": " + err.Error())
			}
			// variable.ZapLog.Info("策略3成功：标准适配器创建成功")
		} else {
			// variable.ZapLog.Info("策略2成功：完整表名适配器创建成功")
		}
	} else {
		// variable.ZapLog.Info("策略1成功：自定义表名适配器创建成功")
	}

	// 验证适配器表名设置
	if a != nil {
		// variable.ZapLog.Info("适配器创建成功，准备验证表结构")
	}
	modelConfig := variable.ConfigYml.GetString("Casbin.ModelConfig")

	// 添加详细的调试信息
	// variable.ZapLog.Info("Casbin模型配置调试信息",
	// 	zap.String("raw_model_config", modelConfig),
	// 	zap.Int("config_length", len(modelConfig)),
	// 	zap.Bool("config_empty", modelConfig == ""))

	// 检查配置中是否包含必要的部分
	hasPolicySection := strings.Contains(modelConfig, "[policy_definition]")
	hasRequestSection := strings.Contains(modelConfig, "[request_definition]")
	hasRoleSection := strings.Contains(modelConfig, "[role_definition]")
	hasEffectSection := strings.Contains(modelConfig, "[policy_effect]")
	hasMatcherSection := strings.Contains(modelConfig, "[matchers]")
	variable.ZapLog.Info("Casbin模型配置检查",
		zap.Bool("has_policy_section", hasPolicySection),
		zap.Bool("has_request_section", hasRequestSection),
		zap.Bool("has_role_section", hasRoleSection),
		zap.Bool("has_effect_section", hasEffectSection),
		zap.Bool("has_matcher_section", hasMatcherSection))

	// 如果配置为空或者缺少关键部分，使用默认配置
	if modelConfig == "" || !hasPolicySection || !hasRequestSection {
		variable.ZapLog.Warn("Casbin模型配置不完整或为空，使用默认配置")
		// 确保每个部分都正确定义，特别是 policy_definition 部分
		modelConfig = `[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub) || p.sub == "*") && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")`
	}

	// 确保模型配置包含所有必要部分
	modelConfig = strings.TrimSpace(modelConfig)

	// 添加更详细的调试信息
	// variable.ZapLog.Info("最终的Casbin模型配置",
	// 	zap.String("final_model_config", modelConfig),
	// 	zap.Int("final_length", len(modelConfig)))

	// 尝试创建模型
	m, err := model.NewModelFromString(modelConfig)
	if err != nil {
		variable.ZapLog.Error("Casbin模型创建失败",
			zap.Error(err),
			zap.String("model_config", modelConfig),
			zap.String("error_detail", err.Error()))

		// 如果模型创建失败，强制使用硬编码的默认模型
		// variable.ZapLog.Warn("强制使用硬编码的默认Casbin模型")
		m = model.NewModel()
		m.AddDef("r", "r", "sub, obj, act")
		m.AddDef("p", "p", "sub, obj, act")
		m.AddDef("g", "g", "_, _")
		m.AddDef("e", "e", "some(where (p.eft == allow))")
		m.AddDef("m", "m", "(g(r.sub, p.sub) || p.sub == '*') && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == '*')")
	}

	// 验证模型是否包含P部分
	if _, exists := m["p"]; !exists || len(m["p"]) == 0 {
		// variable.ZapLog.Error("Casbin模型缺少P部分，添加默认P部分")
		m.AddDef("p", "p", "sub, obj, act")
	}

	// variable.ZapLog.Info("模型结构验证", zap.Any("model_structure", m))
	// variable.ZapLog.Info("Casbin模型创建成功")

	// 添加更详细的执行器创建日志
	// variable.ZapLog.Info("开始创建 Casbin 执行器",
		// zap.String("adapter_type", "gorm-adapter"),
		// zap.String("数据库表", prefix+"_"+tbName))

	if Enforcer, err = casbin.NewSyncedEnforcer(m, a); err != nil {
		variable.ZapLog.Error("Casbin执行器创建失败",
			zap.Error(err),
			zap.String("error_type", "NewSyncedEnforcer"),
			zap.String("error_message", err.Error()),
		)
		return nil, errors.New(my_errors.ErrorCasbinCreateEnforcerFail + ": " + err.Error())
	}

	variable.ZapLog.Info("Casbin执行器创建成功")

	// 增强策略加载，添加重试机制和详细日志
	// variable.ZapLog.Info("开始加载Casbin策略")
	if err := Enforcer.LoadPolicy(); err != nil {
		variable.ZapLog.Error("Casbin策略加载失败，尝试重新加载", zap.Error(err))

		// 重试一次策略加载
		time.Sleep(100 * time.Millisecond)
		if err2 := Enforcer.LoadPolicy(); err2 != nil {
			variable.ZapLog.Error("Casbin策略重新加载也失败", zap.Error(err2))
		} else {
			// variable.ZapLog.Info("Casbin策略重新加载成功")
		}
	} else {
		// variable.ZapLog.Info("Casbin策略加载成功")
	}

	// 验证策略加载结果
	policies, _ := Enforcer.GetPolicy()
	// roles, _ := Enforcer.GetGroupingPolicy()
	// variable.ZapLog.Info("Casbin策略加载验证",
	// 	zap.Int("policy_count", len(policies)),
	// 	zap.Int("role_count", len(roles)),
	// 	zap.String("table_name", fullTableName))

	// 如果策略数为0但表中有数据，尝试诊断和修复
	if len(policies) == 0 {
		// variable.ZapLog.Warn("警告：策略加载成功但策略数量为0，开始诊断表结构")

		// 检查表中实际的数据数量
		var recordCount int64
		countErr := tmpDbConn.Table(fullTableName).Count(&recordCount).Error
		if countErr != nil {
			variable.ZapLog.Error("无法查询表记录数", zap.Error(countErr))
		} else {
			// variable.ZapLog.Info("表记录统计",
			// 	zap.Int64("total_records", recordCount),
			// 	zap.String("table_name", fullTableName))

			// 如果表中有数据但策略为0，可能是表结构问题
			if recordCount > 0 {
				variable.ZapLog.Warn("发现表结构兼容性问题：表中有数据但策略加载为0")

				// 查询表结构信息
				type ColumnInfo struct {
					ColumnName string `gorm:"column:COLUMN_NAME"`
					DataType   string `gorm:"column:DATA_TYPE"`
				}
				var columns []ColumnInfo
				colErr := tmpDbConn.Raw("SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.columns WHERE table_name = ? ORDER BY ORDINAL_POSITION", fullTableName).Scan(&columns).Error
				if colErr != nil {
					variable.ZapLog.Error("无法查询表结构", zap.Error(colErr))
				} else {
					variable.ZapLog.Info("当前表结构", zap.Any("columns", columns))

					// 检查是否有非标准字段
					standardFields := map[string]bool{
						"id": true, "ptype": true, "v0": true, "v1": true,
						"v2": true, "v3": true, "v4": true, "v5": true,
						"created_at": true, "updated_at": true,
					}

					var nonStandardFields []string
					for _, col := range columns {
						if !standardFields[col.ColumnName] {
							nonStandardFields = append(nonStandardFields, col.ColumnName)
						}
					}

					if len(nonStandardFields) > 0 {
						variable.ZapLog.Warn("发现非标准字段",
							zap.Strings("non_standard_fields", nonStandardFields),
							zap.String("建议", "这些字段可能导致Casbin策略加载失败"))
					}

					// 尝试查询部分数据验证
					type PolicyRecord struct {
						PType string `gorm:"column:ptype"`
						V0    string `gorm:"column:v0"`
						V1    string `gorm:"column:v1"`
						V2    string `gorm:"column:v2"`
					}
					var sampleRecords []PolicyRecord
					sampleErr := tmpDbConn.Table(fullTableName).Select("ptype, v0, v1, v2").Limit(5).Find(&sampleRecords).Error
					if sampleErr != nil {
						variable.ZapLog.Error("无法查询样本数据", zap.Error(sampleErr))
					} else {
						// variable.ZapLog.Info("样本数据查询成功",
						// 	zap.Int("sample_count", len(sampleRecords)),
						// 	zap.Any("sample_data", sampleRecords))
					}
				}
			}
		}
	}

	AutoLoadSeconds := variable.ConfigYml.GetDuration("Casbin.AutoLoadPolicySeconds")
	if AutoLoadSeconds > 0 {
		// 使用安全的自动加载，避免panic
		go func() {
			ticker := time.NewTicker(time.Second * AutoLoadSeconds)
			defer ticker.Stop()
			for range ticker.C {
				func() {
					defer func() {
						if r := recover(); r != nil {
							variable.ZapLog.Error("Casbin自动加载策略发生panic", zap.Any("panic", r))
						}
					}()
					if err := Enforcer.LoadPolicy(); err != nil {
						variable.ZapLog.Error("Casbin自动加载策略失败", zap.Error(err))
					}
				}()
			}
		}()
	}
	return Enforcer, nil
}
